package com.hhsjdesign.dynamicui.asm.v3

import com.hhsjdesign.dynamicui.asm.Logger
import org.apache.commons.io.IOUtils
import org.objectweb.asm.*

import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry

/**
 * asm核心注入代码工具类
 */
class RegisterCodeGeneratorV3 {
    ScanSettingV3 scanSettingV3

    private RegisterCodeGeneratorV3(ScanSettingV3 extension) {
        this.scanSettingV3 = extension
    }

    static void insertInitCodeTo(ScanSettingV3 registerSetting) {
        if (registerSetting != null && !registerSetting.classList.isEmpty()) {
            RegisterCodeGeneratorV3 processor = new RegisterCodeGeneratorV3(registerSetting)
            File file = DynamicUiTransformV3.targetAsmClass
            if (file.getName().endsWith('.jar'))
                processor.insertInitCodeIntoJarFile(file)
        }
    }

    /**
     * generate code into jar file
     * @param jarFile the jar file which contains LogisticsCenter.class
     * @return
     */
    private File insertInitCodeIntoJarFile(File jarFile) {
        if (jarFile) {
            def optJar = new File(jarFile.getParent(), jarFile.name + ".opt")
            if (optJar.exists())
                optJar.delete()
            def file = new JarFile(jarFile)
            Enumeration enumeration = file.entries()
            JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(optJar))

            while (enumeration.hasMoreElements()) {
                JarEntry jarEntry = (JarEntry) enumeration.nextElement()
                String entryName = jarEntry.getName()
                ZipEntry zipEntry = new ZipEntry(entryName)
                InputStream inputStream = file.getInputStream(jarEntry)
                jarOutputStream.putNextEntry(zipEntry)
                //找到指定的类文件，就执行字节码操作
                if (ScanSettingV3.GENERATE_TO_CLASS_FILE_NAME == entryName) {

                    Logger.i('Insert init code to class >> ' + entryName)

                    //真正的插入
                    def bytes = asmOperate(inputStream)
                    jarOutputStream.write(bytes)
                } else {
                    jarOutputStream.write(IOUtils.toByteArray(inputStream))
                }
                inputStream.close()
                jarOutputStream.closeEntry()
            }
            jarOutputStream.close()
            file.close()

            if (jarFile.exists()) {
                jarFile.delete()
            }
            optJar.renameTo(jarFile)
        }
        return jarFile
    }

    /**
     * 字节码访问和操作
     * @param inputStream
     * @return
     */
    private byte[] asmOperate(InputStream inputStream) {
        //首先那是一个类，读到ClassReader中
        ClassReader cr = new ClassReader(inputStream)
        //然后用ClassWriter修改
        ClassWriter cw = new ClassWriter(cr, 0)
        //类访问器
        ClassVisitor cv = new ASMClassVisitor(scanSettingV3, Opcodes.ASM6, cw)
        //然后执行，用reader接收writer到操作，
        cr.accept(cv, ClassReader.SKIP_FRAMES)
        //最终砖层字节数组
        return cw.toByteArray()
    }

}

